package com.ruoyi.pcj.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.mashuai.utils.toolkit.core.msConverts.MsConvertDataUtils;
import com.mashuai.utils.toolkit.core.msHandlebean.MsHandleDataUtils;
import com.ruoyi.basis.service.MsBaseServiceImpl;
import com.mashuai.utils.toolkit.core.msMp.MsDb;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.basis.utils.mp.MpConditionUtils;
import com.ruoyi.basis.utils.mp.MpPageUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.pcj.domain.*;
import org.apache.commons.collections4.MapUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

import com.ruoyi.pcj.mapper.PcjTbArticleMapper;
import com.ruoyi.pcj.service.IPcjTbArticleService;
import org.springframework.transaction.annotation.Transactional;

/**
 * 贴吧-文章Service业务层处理
 *
 * @author dk
 * @date 2024-03-02
 */
@Service
public class PcjTbArticleServiceImpl extends MsBaseServiceImpl<PcjTbArticleMapper, PcjTbArticle> implements IPcjTbArticleService {

    // 当前用户id
    private Long currentUserId;
    // 文章作者id
    private Long articleUserId;


    @Override
    public <E extends IPage<PcjTbArticle>> E page(E page, Wrapper<PcjTbArticle> qw) {
        return baseMapper.selectPage(page, qw);
    }

    /**
     * 查询贴吧-文章列表
     *
     * @param params 聊天室-查询参数 {"pageNum":1,"pageSize":10}
     * @return 贴吧-文章
     */
    @Override
    public IPage<PcjTbArticle> selectPcjTbArticleList(Map<String, Object> params) {
        //获取条件构造器
        QueryWrapper<PcjTbArticle> qw = MpConditionUtils.getQueryWrapper(params, PcjTbArticle.class);
        //分页查询
        Page<PcjTbArticle> page = this.page(MpPageUtils.getPage(params), qw);
        List<PcjTbArticle> pcjTbArticles = page.getRecords();
        // 判断文章有没有被当前用户点赞或收藏
        likeTrueOrFalse(pcjTbArticles);
        // 查找文章关联的话题
        selectArticleSubject(pcjTbArticles);
        MsHandleDataUtils.handleTbOrDictValBeans(page.getRecords(), PcjTbArticle.class);
        return page;
    }

    private void likeTrueOrFalse(List<PcjTbArticle> pcjTbArticles) {
        Long userId = SecurityUtils.getUserId();
        for (PcjTbArticle pcjTbArticle : pcjTbArticles) {
            String id = pcjTbArticle.getId();
            LambdaQueryWrapper<PcjTbLike> qw = Wrappers.lambdaQuery();
            qw.eq(PcjTbLike::getArticleId, id);
            qw.eq(PcjTbLike::getUserId, userId);
            PcjTbLike tbLike = MsDb.getOne(qw, PcjTbLike.class);
            pcjTbArticle.setLikeFlag(null != tbLike);

            LambdaQueryWrapper<PcjTbCollect> qw2 = Wrappers.lambdaQuery();
            qw2.eq(PcjTbCollect::getArticleId, id);
            qw2.eq(PcjTbCollect::getUserId, userId);
            PcjTbCollect tbCollect = MsDb.getOne(qw2, PcjTbCollect.class);
            pcjTbArticle.setCollectFlag(null != tbCollect);
        }
    }

    private void selectArticleSubject(List<PcjTbArticle> pcjTbArticles) {
        for (PcjTbArticle pcjTbArticle : pcjTbArticles) {
            String articleId = pcjTbArticle.getId();
            LambdaQueryWrapper<PcjTbArticleSubject> qw = Wrappers.lambdaQuery();
            qw.eq(PcjTbArticleSubject::getArticleId, articleId);
            List<PcjTbArticleSubject> subjects = MsDb.list(qw, PcjTbArticleSubject.class);
            ArrayList<String> subjectList = new ArrayList<>();
            for (PcjTbArticleSubject subject : subjects) {
                LambdaQueryWrapper<PcjTbSubject> qw2 = Wrappers.lambdaQuery();
                qw2.eq(PcjTbSubject::getId, subject.getSubjectId());
                PcjTbSubject tbSubject = MsDb.getOne(qw2, PcjTbSubject.class);
                subjectList.add(tbSubject.getName());
            }
            pcjTbArticle.setSubjects(subjectList);
        }
    }

    /**
     * 根据id查询贴吧-文章
     *
     * @param id 贴吧-文章主键
     * @return 贴吧-文章
     */
    @Override
    public PcjTbArticle selectPcjTbArticleById(String id) {
        //首先获取主表信息
        PcjTbArticle pcjTbArticle = MsDb.getById(id, PcjTbArticle.class);
        MsHandleDataUtils.handleTbOrDictValBeans(pcjTbArticle, PcjTbArticle.class);

        //获取文章对应的图片
        List<PcjTbFile> articleFiles = getPcjTbFilesById(id);
        List<String> articleFilesList = getFilesList(articleFiles);
        pcjTbArticle.setFileUrls(articleFilesList);

        // 判断文章有没有被当前用户点赞或收藏
        likeTrueOrFalse(Collections.singletonList(pcjTbArticle));
        // 查找文章关联的话题
        selectArticleSubject(Collections.singletonList(pcjTbArticle));

        return pcjTbArticle;
    }

    @Override
    public PcjTbArticle selectPcjTbArticleCommentById(String id) {
        PcjTbArticle pcjTbArticle = MsDb.getById(id, PcjTbArticle.class);
        //获取一级评论
        List<PcjTbComment> comments = getCommentsByArticleId(id);
        for (PcjTbComment comment : comments) {
            //获取一级评论对应的图片
            List<PcjTbFile> commentFiles = getPcjTbFilesById(comment.getId());
            List<String> commentFilesList = getFilesList(commentFiles);
            comment.setFileUrls(commentFilesList);
            //获取一级评论对应的二级评论
            List<PcjTbCommentReply> commentReplies = getCommentReplyByCommentId(comment.getId());
            //获取二级评论对应的图片
            for (PcjTbCommentReply commentReply : commentReplies) {
                List<PcjTbFile> commentReplyFiles = getPcjTbFilesById(commentReply.getId());
                List<String> commentReplyFilesList = getFilesList(commentReplyFiles);
                commentReply.setFileUrls(commentReplyFilesList);
            }
            comment.setPcjTbCommentReplyList(commentReplies);
        }
        pcjTbArticle.setPcjTbCommentList(comments);
        return pcjTbArticle;
    }


    @NotNull
    private static List<String> getFilesList(List<PcjTbFile> commentFiles) {
        return commentFiles.stream()
                .map(PcjTbFile::getFileUrl)
                .collect(Collectors.toList());
    }

    /**
     * 新增贴吧-文章
     *
     * @param pcjTbArticle 贴吧-文章
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean insertPcjTbArticle(PcjTbArticle pcjTbArticle) {
        //插入主表
        Boolean success = this.insertData(pcjTbArticle);
        //插入图片表
        insertTbFile(pcjTbArticle.getFileUrls(), pcjTbArticle.getId());
        //插入话题表
        insertSubject(pcjTbArticle.getSubjects(), pcjTbArticle.getId());
        return success;
    }

    private void insertSubject(List<String> subjects, String id) {
        if (!subjects.isEmpty()) {
            ArrayList<PcjTbArticleSubject> list = new ArrayList<>();
            for (String subject : subjects) {
                LambdaQueryWrapper<PcjTbSubject> qw = Wrappers.lambdaQuery();
                qw.eq(PcjTbSubject::getName, subject);
                PcjTbSubject tbSubject = MsDb.getOne(qw, PcjTbSubject.class);
                PcjTbArticleSubject articleSubject = new PcjTbArticleSubject();
                if (null != tbSubject) {
                    articleSubject.setArticleId(id);
                    articleSubject.setSubjectId(tbSubject.getId());
                } else {
                    PcjTbSubject pcjTbSubject = new PcjTbSubject();
                    pcjTbSubject.setName(subject);
                    pcjTbSubject.setUserId(SecurityUtils.getUserId());
                    MsDb.save(pcjTbSubject);
                    articleSubject.setArticleId(id);
                    articleSubject.setSubjectId(pcjTbSubject.getId());
                }
                list.add(articleSubject);
            }
            MsDb.saveBatch(list);
        }
    }

    private void insertTbFile(List<String> fileUrls, String bizId) {
        if (!fileUrls.isEmpty()) {
            ArrayList<PcjTbFile> list = new ArrayList<>();
            for (String fileUrl : fileUrls) {
                PcjTbFile tbFile = initPcjTbFile(bizId, fileUrl);
                list.add(tbFile);
            }
            MsDb.saveBatch(list);
        }
    }

    @NotNull
    private static PcjTbFile initPcjTbFile(String bizId, String fileUrl) {
        PcjTbFile tbFile = new PcjTbFile();
        tbFile.setUserId(SecurityUtils.getUserId());
        tbFile.setType("0");
        tbFile.setBizId(bizId);
        tbFile.setFileUrl(fileUrl);
        return tbFile;
    }

    /**
     * 修改贴吧-文章
     *
     * @param pcjTbArticle 贴吧-文章
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean updatePcjTbArticle(PcjTbArticle pcjTbArticle) {
        //修改主表
        Boolean success = this.updateData(pcjTbArticle);
        //修改图片表
        updateTbFile(pcjTbArticle.getId());
        return success;
    }

    private void updateTbFile(String id) {
        List<PcjTbFile> pcjTbFiles = getPcjTbFilesById(id);
        if (!pcjTbFiles.isEmpty()) {
            for (PcjTbFile pcjTbFile : pcjTbFiles) {
                MsDb.removeById(pcjTbFile);
            }
            MsDb.saveBatch(pcjTbFiles);
        }
    }

    private static List<PcjTbFile> getPcjTbFilesById(String id) {
        LambdaQueryWrapper<PcjTbFile> qw = Wrappers.lambdaQuery();
        qw.eq(PcjTbFile::getBizId, id);
        return MsDb.list(qw, PcjTbFile.class);
    }

    /**
     * 批量删除贴吧-文章
     *
     * @param ids 需要删除的贴吧-文章主键
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean deletePcjTbArticleByIds(String[] ids) {
        //删除主表
        Boolean success = this.removeBatchByIds(Arrays.asList(ids));
        //删除图片表
        for (String id : ids) {
            List<PcjTbFile> pcjTbFiles = getPcjTbFilesById(id);
            for (PcjTbFile pcjTbFile : pcjTbFiles) {
                MsDb.removeById(pcjTbFile);
            }
        }
        return success;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deletePcjTbArticleById(String id) {
        // 根据文章id找到所有的评论
        List<PcjTbComment> list = getCommentsByArticleId(id);
        for (PcjTbComment pcjTbComment : list) {
            // 删除一级评论,一级评论id
            String commentId = pcjTbComment.getId();
            MsDb.removeById(commentId, PcjTbComment.class);
            //删除图片表
            commonDeleteTbFiles(commentId);
            // 删除二级评论的图片
            deleteCommentReplyFiles(commentId);
            // 统计二级评论数量并删除
            countCommentAndDelete(commentId);
        }
        //删除文章对应的图片表数据
        commonDeleteTbFiles(id);
        //删除主表
        return this.removeById(id);
    }

    private static List<PcjTbComment> getCommentsByArticleId(String id) {
        LambdaQueryWrapper<PcjTbComment> qw = Wrappers.lambdaQuery();
        qw.eq(PcjTbComment::getArticleId, id);
        List<PcjTbComment> list = MsDb.list(qw, PcjTbComment.class);
        return list;
    }

    /**
     * 根据主表id，删除子表所有数据
     **/
    /**
     * 对外提供的设置默认字段值，id为空新增，不为空更新
     **/
    public void setEntityDefValAuto(PcjTbArticle pcjTbArticle) {
        this.setDefFieldVal(pcjTbArticle);
    }

    /**
     * 文章点赞
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateArticleLike(Map<String, Object> params) {
        String id = MapUtils.getString(params, "id");
        boolean likeFlag = MapUtils.getBoolean(params, "likeFlag");
        // 更新点赞量
        updateArticleBase(id, PcjTbArticle::getLikeNum, PcjTbArticle::setLikeNum, likeFlag ? 1 : -1);
        // 写入点赞表
        PcjTbLike pcjTbLike = null;
        String routePath = MapUtils.getString(params, "routePath");
        if (likeFlag) {
            pcjTbLike = insertSonTable(id, new Date(), null, null, null, PcjTbLike.class);
            // 写入消息通知表
            insertMsgNotice("0", pcjTbLike.getId(), articleUserId, "赞了你的文章", routePath);
        } else {
            // 删除点赞表
            LambdaQueryWrapper<PcjTbLike> qw = Wrappers.lambdaQuery();
            qw.eq(PcjTbLike::getArticleId, id);
            qw.eq(PcjTbLike::getUserId, SecurityUtils.getUserId());
            pcjTbLike = MsDb.getOne(qw, PcjTbLike.class);
            MsDb.remove(qw, PcjTbLike.class);
            // 写入消息通知表
            insertMsgNotice("0", pcjTbLike.getId(), articleUserId, "取消了你的文章点赞", routePath);
        }
    }

    /**
     * @return java.lang.Boolean
     * @Author Jdike
     * @Description 文章收藏
     * @Date 21:46 2024/3/13
     * @Param [params]
     **/
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateArticleCollect(Map<String, Object> params) {
        String id = MapUtils.getString(params, "id");
        boolean collectFlag = MapUtils.getBoolean(params, "collectFlag");
        // 更新收藏量
        updateArticleBase(id, PcjTbArticle::getCollectNum, PcjTbArticle::setCollectNum, collectFlag ? 1 : -1);
        // 写入收藏表
        PcjTbCollect pcjTbCollect = null;
        String routePath = MapUtils.getString(params, "routePath");
        if (collectFlag) {
            pcjTbCollect = insertSonTable(id, null, new Date(), null, null, PcjTbCollect.class);
            // 写入消息通知表
            insertMsgNotice("1", pcjTbCollect.getId(), articleUserId, "收藏了你的文章", routePath);
        } else {
            LambdaQueryWrapper<PcjTbCollect> qw = Wrappers.lambdaQuery();
            qw.eq(PcjTbCollect::getArticleId, id);
            qw.eq(PcjTbCollect::getUserId, SecurityUtils.getUserId());
            pcjTbCollect = MsDb.getOne(qw, PcjTbCollect.class);
            MsDb.remove(qw, PcjTbCollect.class);
            // 写入消息通知表
            insertMsgNotice("1", pcjTbCollect.getId(), articleUserId, "取消了你的文章收藏", routePath);
        }
    }

    /**
     * @return void
     * @Author Jdike
     * @Description 一级评论保存
     * @Date 23:02 2024/3/13
     * @Param [params]
     **/
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveArticleComment(Map<String, Object> params) {
        // 文章id
        String id = MapUtils.getString(params, "id");
        String content = MapUtils.getString(params, "content");
        // 更新评论量
        updateArticleBase(id, PcjTbArticle::getCommentNum, PcjTbArticle::setCommentNum, 1);
        // 写入一级评论表
        PcjTbComment pcjTbComment = insertSonTable(id, null, null, content, null, PcjTbComment.class);
        // 写入图片表，一级评论的id
        List<String> list = (List<String>) MapUtils.getObject(params, "fileUrls");
        insertTbFile(list, pcjTbComment.getId());
        // 写入消息通知表，一级评论的id
        String routePath = MapUtils.getString(params, "routePath");
        insertMsgNotice("2", pcjTbComment.getId(), articleUserId, "评论了你的文章", routePath);
    }

    private static int countCommentAndDelete(String id) {
        LambdaQueryWrapper<PcjTbCommentReply> qw = Wrappers.lambdaQuery();
        qw.eq(PcjTbCommentReply::getCommentId, id);
        int commentCount = (int) MsDb.count(qw, PcjTbCommentReply.class);
        MsDb.remove(qw, PcjTbCommentReply.class);
        return commentCount;
    }

    /**
     * @return void
     * @Author Jdike
     * @Description 二级评论保存
     * @Date 23:29 2024/3/13
     * @Param [params]
     **/
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveArticleCommentReply(Map<String, Object> params) {
        String commentId = MapUtils.getString(params, "comment_id");
        String content = MapUtils.getString(params, "content");
        PcjTbComment pcjTbComment = MsDb.getById(commentId, PcjTbComment.class);
        String articleId = pcjTbComment.getArticleId();
        // 更新评论量
        updateArticleBase(articleId, PcjTbArticle::getCommentNum, PcjTbArticle::setCommentNum, 1);
        // 写入二级评论表
        PcjTbCommentReply pcjTbCommentReply = insertSonTable(null, null, null, content, commentId, PcjTbCommentReply.class);
        // 写入消息通知表
        String routePath = MapUtils.getString(params, "routePath");
        insertMsgNotice("2", articleId, articleUserId, "评论了你的文章", routePath);
        insertMsgNotice("2", pcjTbCommentReply.getId(), pcjTbComment.getUserId(), "回复了你的评论", routePath);
        // 写入图片表
        List<String> list = (List<String>) MapUtils.getObject(params, "fileUrls");
        insertTbFile(list, pcjTbCommentReply.getId());
    }

    /**
     * @return void
     * @Author Jdike
     * @Description 一级评论删除
     * @Date 23:43 2024/3/14
     * @Param [params]
     **/
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteArticleComment(Map<String, Object> params) {
        // 删除一级评论,一级评论id
        String id = MapUtils.getString(params, "id");
        MsDb.removeById(id, PcjTbComment.class);
        //删除图片表
        commonDeleteTbFiles(id);
        // 删除二级评论的图片
        deleteCommentReplyFiles(id);
        // 统计二级评论数量并删除
        int commentCount = countCommentAndDelete(id);
        // 根据文章id更新评论数
        String articleId = MapUtils.getString(params, "article_id");
        int commentNum = commentCount + 1;
        updateArticleBase(articleId, PcjTbArticle::getCommentNum, PcjTbArticle::setCommentNum, -commentNum);
        // 写入消息通知表
        String routePath = MapUtils.getString(params, "routePath");
        insertMsgNotice("2", id, articleUserId, "删除了评论", routePath);
    }

    private void deleteCommentReplyFiles(String id) {
        // 根据一级评论id找到所有的二级评论
        List<PcjTbCommentReply> list = getCommentReplyByCommentId(id);
        // 根据二级评论id删除file
        for (PcjTbCommentReply pcjTbCommentReply : list) {
            commonDeleteTbFiles(pcjTbCommentReply.getId());
        }
    }

    private static List<PcjTbCommentReply> getCommentReplyByCommentId(String id) {
        LambdaQueryWrapper<PcjTbCommentReply> qw = Wrappers.lambdaQuery();
        qw.eq(PcjTbCommentReply::getCommentId, id);
        List<PcjTbCommentReply> list = MsDb.list(qw, PcjTbCommentReply.class);
        return list;
    }

    /**
     * @return void
     * @Author Jdike
     * @Description 二级评论删除
     * @Date 23:43 2024/3/14
     * @Param [params]
     **/
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteArticleCommentReply(Map<String, Object> params) {
        // 删除二级评论,二级评论id
        String id = MapUtils.getString(params, "id");
        MsDb.removeById(id, PcjTbCommentReply.class);
        // 获取文章id
        String commentId = MapUtils.getString(params, "comment_id");
        PcjTbComment pcjTbComment = MsDb.getById(commentId, PcjTbComment.class);
        String articleId = pcjTbComment.getArticleId();
        // 更新文章评论量
        updateArticleBase(articleId, PcjTbArticle::getCommentNum, PcjTbArticle::setCommentNum, -1);
        // 写入消息通知表
        String routePath = MapUtils.getString(params, "routePath");
        insertMsgNotice("2", articleId, articleUserId, "删除了评论", routePath);
        insertMsgNotice("2", id, pcjTbComment.getUserId(), "删除了回复", routePath);
        //删除图片表
        commonDeleteTbFiles(id);
    }

    private void commonDeleteTbFiles(String bizId) {
        List<PcjTbFile> pcjTbFiles = getPcjTbFilesById(bizId);
        for (PcjTbFile pcjTbFile : pcjTbFiles) {
            MsDb.removeById(pcjTbFile);
        }
    }

    /**
     * @return void
     * @Author Jdike
     * @Description 更新article表中的点赞、收藏、评论数量
     * @Date 0:18 2024/3/14
     * @Param [id, getAttr, setAttr, opNum]
     **/
    private void updateArticleBase(String id, SFunction<PcjTbArticle, Integer> getAttr, BiConsumer<PcjTbArticle, Integer> setAttr, Integer opNum) {
        PcjTbArticle article = MsDb.getById(id, PcjTbArticle.class);
        articleUserId = article.getUserId();
        Integer curNum = getAttr.apply(article);
        setAttr.accept(article, curNum + opNum);
        MsDb.updateById(article);
    }

    /**
     * @return void
     * @Author Jdike
     * @Description 子表入库
     * @Date 0:19 2024/3/14
     * @Param [articleId, likeTime, collectTime, content, commentId, sonClz]
     **/
    public <T> T insertSonTable(String articleId, Date likeTime, Date collectTime, String content, String commentId, Class<T> sonClz) {
        Map<String, Object> map = new HashMap<>();
        currentUserId = SecurityUtils.getUserId();
        map.put("user_id", currentUserId);
        if (StringUtils.isNotBlank(articleId)) {
            map.put("article_id", articleId);
        }
        if (null != likeTime) {
            map.put("like_time", likeTime);
        }
        if (null != collectTime) {
            map.put("collect_time", collectTime);
        }
        if (StringUtils.isNotBlank(content)) {
            map.put("content", content);
        }
        if (StringUtils.isNotBlank(commentId)) {
            map.put("comment_id", commentId);
        }
        T t = MsConvertDataUtils.mapToBean(map, sonClz);
        MsDb.save(t);
        return t;
    }

    /**
     * @return void
     * @Author Jdike
     * @Description
     * @Date 17:02 2024/3/17
     * @Param [type:通知类型，0-点赞，1-收藏，2-评论，3-好友通知，4-系统通知
     * bizId:业务单据id，比如贴吧存文章id，好友通知存好友id,
     * noticeUserId:通知的用户id,
     * routePath:路由路径，根据类型不同，可以跳到对应的页面
     * ]
     **/
    public void insertMsgNotice(String type, String bizId, Long noticeUserId, String content, String routePath) {
        PcjMsgNotice msgNotice = new PcjMsgNotice();
        msgNotice.setOperateUserId(SecurityUtils.getUserId());
        msgNotice.setType(type);
        msgNotice.setBizId(bizId);
        msgNotice.setNoticeUserId(noticeUserId);
        msgNotice.setContent(content);
        msgNotice.setReadStatus("0");
        msgNotice.setNoticeTime(new Date());
        msgNotice.setRoutePath(routePath);
        MsDb.save(msgNotice);
    }
}
